/*
* Copyright (c) 2021 Talkweb Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "service_http.h"

static void _service_http_exec_inc(service_http_ctx_t *http_ctx)
{
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->service_exec_count++;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
}

static void _service_http_exec_dec(service_http_ctx_t *http_ctx)
{
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->service_exec_count--;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
}


static void _service_tiot_http_exec_inc(service_http_ctx_t *http_ctx)
{
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->exec_count++;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
}

static void _service_tiot_http_exec_dec(service_http_ctx_t *http_ctx)
{
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->exec_count--;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
}

static void _service_http_auth_recv_ctx(void *ctx, const tiot_http_recv_t *packet, void *userdata)
{
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;
    service_http_response_t *response = (service_http_response_t *)userdata;

    switch (packet->type) {
        case TIOT_HTTPRECV_STATUS_CODE: {
            response->code = packet->data.status_code.code;
        }
        break;
        case TIOT_HTTPRECV_HEADER: {
            if ((strlen(packet->data.header.key) == strlen("Content-Length")) &&
                (memcmp(packet->data.header.key, "Content-Length", strlen(packet->data.header.key)) == 0)) {
                service_str2uint(packet->data.header.value, (uint8_t)strlen(packet->data.header.value), &response->content_total_len);
            }
        }
        break;
        case TIOT_HTTPRECV_BODY: {
            uint8_t *content = http_ctx->sysdep->service_sysdep_malloc(response->content_len + packet->data.body.len + 1);
            if (content == NULL) {
                return;
            }
            memset(content, 0, response->content_len + packet->data.body.len + 1);
            if (response->content != NULL) {
                memcpy(content, response->content, response->content_len);
                http_ctx->sysdep->service_sysdep_free(response->content);
            }
            memcpy(content + response->content_len, packet->data.body.buffer, packet->data.body.len);
            response->content = content;
            response->content_len = response->content_len + packet->data.body.len;
        }
        break;
        default: {

        }
        break;
    }
}

static void _service_http_recv_ctx(void *ctx, const tiot_http_recv_t *packet, void *userdata)
{
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;
    service_http_response_t *response = (service_http_response_t *)userdata;

    if (http_ctx->recv_ctx == NULL) {
        return;
    }

    switch (packet->type) {
        case TIOT_HTTPRECV_STATUS_CODE: {
            http_ctx->recv_ctx(http_ctx, packet, http_ctx->userdata);
        }
        break;
        case TIOT_HTTPRECV_HEADER: {
            if ((strlen(packet->data.header.key) == strlen("Content-Length")) &&
                (memcmp(packet->data.header.key, "Content-Length", strlen(packet->data.header.key)) == 0)) {
                service_str2uint(packet->data.header.value, (uint8_t)strlen(packet->data.header.value), &response->content_total_len);
            }
            http_ctx->recv_ctx(http_ctx, packet, http_ctx->userdata);
        }
        break;
        case TIOT_HTTPRECV_BODY: {
            uint8_t *content = http_ctx->sysdep->service_sysdep_malloc(response->content_len + packet->data.body.len + 1);
            if (content == NULL) {
                return;
            }
            memset(content, 0, response->content_len + packet->data.body.len + 1);
            if (response->content != NULL) {
                memcpy(content, response->content, response->content_len);
                http_ctx->sysdep->service_sysdep_free(response->content);
            }
            memcpy(content + response->content_len, packet->data.body.buffer, packet->data.body.len);
            response->content = content;
            response->content_len = response->content_len + packet->data.body.len;
        }
        break;
        default: {

        }
        break;
    }
}

static void _service_tiot_http_token_expired_event(service_http_ctx_t *http_ctx, service_http_response_t *response)
{
    int32_t res = RET_SUCCESS;
    char *code_str = NULL;
    uint32_t code_strlen = 0, code = 0;

    res = service_json_value(http_ctx->sysdep, (const char *)response->content, response->content_len, "code", strlen("code"), &code_str,
                          &code_strlen);
    if (res < RET_SUCCESS) {
        return;
    }

    res = service_str2uint(code_str, code_strlen, &code);
    if (res < RET_SUCCESS) {
        return;
    }

    if (code == TIOT_HTTP_RSPCODE_TOKEN_EXPIRED ||
        code == TIOT_HTTP_RSPCODE_TOKEN_CHECK_ERROR) {
        if (http_ctx->event_ctx != NULL) {
            tiot_http_event_t event;
            event.type = TIOT_HTTPEVT_TOKEN_INVALID;

            http_ctx->event_ctx(http_ctx, &event, http_ctx->userdata);
        }
    }
}

static int32_t _service_http_send_auth(service_http_ctx_t *http_ctx)
{
    int32_t res = RET_SUCCESS;
    char *content = NULL, *path = NULL;
    char *path_fmt = "/gateway/twiot-device-server/device/register";
    char *path_src[] = { path_fmt};
    service_http_request_t request;

    res = service_auth_http_body(http_ctx->sysdep, &content, http_ctx->product_key, http_ctx->device_name,
                              http_ctx->device_secret);
    if (res < RET_SUCCESS) {
        return res;
    }

    res = service_sprintf(http_ctx->sysdep, &path, "%s", path_src, sizeof(path_src) / sizeof(char *));
    if (res < RET_SUCCESS) {
        http_ctx->sysdep->service_sysdep_free(content);
        return res;
    }

    memset(&request, 0, sizeof(service_http_request_t));
    request.method = "POST";
    request.path = path;
    request.header = (http_ctx->long_connection == 0) ? ("Content-Type: application/json\r\nConnection: close\r\n") :
                     ("Content-Type: application/json\r\n");
    request.content = (uint8_t *)content;
    request.content_len = (uint32_t)strlen(content);

    res = service_http_send(http_ctx, &request);

    http_ctx->sysdep->service_sysdep_free(path);
    http_ctx->sysdep->service_sysdep_free(content);

    return res;
}

static int32_t _service_http_recv_auth(service_http_ctx_t *http_ctx, service_http_response_t *response)
{
    int32_t res = RET_SUCCESS;
    char *token = NULL;
    uint32_t token_len = 0;
    uint64_t timenow_ms = http_ctx->sysdep->service_sysdep_time();

    while (1) {
        if (timenow_ms >= http_ctx->sysdep->service_sysdep_time()) {
            timenow_ms = http_ctx->sysdep->service_sysdep_time();
        }
        if (http_ctx->sysdep->service_sysdep_time() - timenow_ms >= http_ctx->auth_timeout_ms) {
            break;
        }

        res = service_http_recv(http_ctx);
        if (res < RET_SUCCESS) {
            break;
        }
    }

    if (res < RET_SUCCESS) {
        if (res != RET_HTTP_READ_BODY_FINISHED) {
            if(res == RET_HTTP_READ_BODY_FINISHED_CHUNCED){
                return RET_SUCCESS;
            }
            return res;
        } else {
            res = RET_SUCCESS;
        }
    } else {
        return RET_HTTP_AUTH_NOT_FINISHED;
    }

    if (response->code != 200) {
        return RET_HTTP_AUTH_CODE_FAILED;
    }
    if (response->content == NULL || response->content_len != response->content_total_len) {
        return RET_HTTP_AUTH_NOT_EXPECTED;
    }
    log_debug("%.*s\r\n", response->content_len, response->content);

    res = service_json_value(http_ctx->sysdep, (const char *)response->content, response->content_len, "token", strlen("token"), &token,
                          &token_len);
    if (res < RET_SUCCESS) {
        return RET_HTTP_AUTH_TOKEN_FAILED;
    }

    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    if (http_ctx->token != NULL) {
        http_ctx->sysdep->service_sysdep_free(http_ctx->token);
        http_ctx->token = NULL;
    }
    http_ctx->token = http_ctx->sysdep->service_sysdep_malloc(token_len + 1);
    if (http_ctx->token == NULL) {
        http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
        return RET_SYS_DEPEND_MALLOC_FAILED;
    }
    memset(http_ctx->token, 0, token_len + 1);
    memcpy(http_ctx->token, token, token_len);
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);

    return RET_SUCCESS;
}

void *tiot_http_init(void)
{
    service_http_ctx_t *http_ctx = NULL;

    http_ctx = service_http_init();
    if (http_ctx == NULL) {
        return NULL;
    }

    http_ctx->auth_timeout_ms = SERVICE_HTTP_DEFAULT_AUTH_TIMEOUT_MS;
    http_ctx->long_connection = 1;

    http_ctx->exec_enabled = 1;

    return http_ctx;
}

/* set host */
int32_t tiot_http_set_host(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;
    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }
    if (http_ctx->service_exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    res = service_strdup(http_ctx->sysdep, &http_ctx->host, (char *)data);
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_http_exec_dec(http_ctx);
    
    return res;
}

/* set port */
int32_t tiot_http_set_port(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }
    if (http_ctx->service_exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->port = *(uint16_t *)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_http_exec_dec(http_ctx);

    return res;
}

/* set network cred */
int32_t tiot_http_network_cred(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }
    if (http_ctx->service_exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    if (http_ctx->cred != NULL) {
        http_ctx->sysdep->service_sysdep_free(http_ctx->cred);
        http_ctx->cred = NULL;
    }
    http_ctx->cred = http_ctx->sysdep->service_sysdep_malloc(sizeof(tiot_sysdep_network_cred_t));
    if (http_ctx->cred != NULL) {
        memset(http_ctx->cred, 0, sizeof(tiot_sysdep_network_cred_t));
        memcpy(http_ctx->cred, data, sizeof(tiot_sysdep_network_cred_t));
    }
    else {
        res = RET_SYS_DEPEND_MALLOC_FAILED;
    }
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_http_exec_dec(http_ctx);

    return res;
}

/* set connect_timeout_ms */
int32_t tiot_http_connect_timeout_ms(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }
    if (http_ctx->service_exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->connect_timeout_ms = *(uint32_t *)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_http_exec_dec(http_ctx);

    return res;
}

/* set send_timeout_ms */
int32_t tiot_http_send_timeout_ms(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }
    if (http_ctx->service_exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->send_timeout_ms = *(uint32_t *)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_http_exec_dec(http_ctx);

    return res;
}


/* set recv_timeout_ms */
int32_t tiot_http_recv_timeout_ms(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }
    if (http_ctx->service_exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->recv_timeout_ms = *(uint32_t *)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_http_exec_dec(http_ctx);

    return res;
}

/* set deinit_timeout_ms */
int32_t tiot_http_deinit_timeout_ms(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }
    if (http_ctx->service_exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->deinit_timeout_ms = *(uint32_t *)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_http_exec_dec(http_ctx);

    return res;
}


/* set header_buffer_len */
int32_t tiot_http_header_buffer_len(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }
    if (http_ctx->service_exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->header_line_max_len = *(uint32_t *)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_http_exec_dec(http_ctx);

    return res;
}

/* set body_buffer_len */
int32_t tiot_http_body_buffer_len (void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }
    if (http_ctx->service_exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->body_buffer_max_len = *(uint32_t *)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_http_exec_dec(http_ctx);

    return res;
}

/* set event_ctx */
int32_t tiot_http_event_ctx(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }
    if (http_ctx->service_exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->event_ctx = (tiot_http_event_ctx_t)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_http_exec_dec(http_ctx);

    return res;
}


//修改那些没有调用其他函数的
/* set userdata */
int32_t tiot_http_userdata(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->userdata = data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_tiot_http_exec_dec(http_ctx);

    return res;
}

/* set recv_ctx */
int32_t tiot_http_recv_ctx(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->recv_ctx = (tiot_http_recv_ctx_t)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_tiot_http_exec_dec(http_ctx);

    return res;
}

/* set product_key */
int32_t tiot_http_set_product_key(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    res = service_strdup(http_ctx->sysdep, &http_ctx->product_key, (char *)data);
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_tiot_http_exec_dec(http_ctx);

    return res;
}


/* set device_name */
int32_t tiot_http_set_device_name(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    res = service_strdup(http_ctx->sysdep, &http_ctx->device_name, (char *)data);
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_tiot_http_exec_dec(http_ctx);

    return res;
}

/* set device_secret */
int32_t tiot_http_set_device_secret(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    res = service_strdup(http_ctx->sysdep, &http_ctx->device_secret, (char *)data);
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_tiot_http_exec_dec(http_ctx);

    return res;
}

/* set extend_devinfo */
int32_t tiot_http_extend_devinfo(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    res = service_strdup(http_ctx->sysdep, &http_ctx->extend_devinfo, (char *)data);
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_tiot_http_exec_dec(http_ctx);

    return res;
}

/* set auth_timeout_ms */
int32_t tiot_http_auth_timeout_ms(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->auth_timeout_ms = *(uint32_t *)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_tiot_http_exec_dec(http_ctx);

    return res;
}


/* set long_connection */
int32_t tiot_http_long_connection(void *ctx, void *data)
{
    int32_t res = RET_SUCCESS;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || data == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);
    http_ctx->sysdep->service_sysdep_mutex_lock(http_ctx->all_mutex.data_mutex);
    http_ctx->long_connection = *(uint8_t *)data;
    http_ctx->sysdep->service_sysdep_mutex_unlock(http_ctx->all_mutex.data_mutex);
    _service_tiot_http_exec_dec(http_ctx);

    return res;
}

int32_t tiot_http_auth(void *ctx)
{
    int32_t res = RET_SUCCESS;
    service_http_response_t response;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }

    if (http_ctx->product_key == NULL) {
        return RET_USER_INPUT_MISSING_PRODUCT_KEY;
    }

    if (http_ctx->device_name == NULL) {
        return RET_USER_INPUT_MISSING_DEVICE_NAME;
    }

    if (http_ctx->device_secret == NULL) {
        return RET_USER_INPUT_MISSING_DEVICE_SECRET;
    }

    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);

    memset(&response, 0, sizeof(service_http_response_t));

    if ((res = service_http_setopt(http_ctx, SERVICE_HTTPOPT_RECV_HANDLER,
                                (void *)_service_http_auth_recv_ctx)) < RET_SUCCESS ||
        (res = service_http_setopt(http_ctx, SERVICE_HTTPOPT_USERDATA, (void *)&response)) < RET_SUCCESS) {
        return res;
    }
    if (http_ctx->network_ctx == NULL ||
        (http_ctx->network_ctx != NULL && http_ctx->long_connection == 0)) {
        if ((res = service_http_connect(http_ctx)) < RET_SUCCESS) {
            _service_tiot_http_exec_dec(http_ctx);
            return res;
        }
    }

    /* send auth request */
    res = _service_http_send_auth(http_ctx);
    if (res < RET_SUCCESS) {
        _service_tiot_http_exec_dec(http_ctx);
        return res;
    }

    /* recv auth response */
    res = _service_http_recv_auth(http_ctx, &response);
    if (response.content != NULL) {
        http_ctx->sysdep->service_sysdep_free(response.content);
    }

    _service_tiot_http_exec_dec(http_ctx);

    return res;
}

int32_t tiot_http_send(void *ctx, char *topic, uint8_t *payload, uint32_t payload_len)
{
    int32_t res = RET_SUCCESS;
    char *path = NULL, *header = NULL;
    char *header_src[] = { NULL, NULL };
    service_http_request_t request;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL || topic == NULL || payload == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (payload_len == 0) {
        return RET_USER_INPUT_OUT_RANGE;
    }
    if (http_ctx->token == NULL) {
        return RET_HTTP_NEED_AUTH;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);

    if (http_ctx->network_ctx == NULL ||
        (http_ctx->network_ctx != NULL && http_ctx->long_connection == 0)) {
        if ((res = service_http_connect(http_ctx)) < RET_SUCCESS) {
            _service_tiot_http_exec_dec(http_ctx);
            return res;
        }
    }

    /* path */
    res = service_sprintf(http_ctx->sysdep, &path, "/topic%s", (char **)&topic, 1);
    if (res < RET_SUCCESS) {
        _service_tiot_http_exec_dec(http_ctx);
        return res;
    }

    /* header */
    header_src[0] = http_ctx->token;
    if (http_ctx->long_connection == 0) {
        header_src[1] = "Connection: close\r\n";
    }
    res = service_sprintf(http_ctx->sysdep, &header, "Content-Type: application/octet-stream\r\nPassword: %s\r\n%s",
                       header_src, sizeof(header_src) / sizeof(char *));
    if (res < RET_SUCCESS) {
        http_ctx->sysdep->service_sysdep_free(path);
        _service_tiot_http_exec_dec(http_ctx);
        return res;
    }

    memset(&request, 0, sizeof(service_http_request_t));
    request.method = "POST";
    request.path = path;
    request.header = header;
    request.content = (uint8_t *)payload;
    request.content_len = payload_len;

    res = service_http_send(http_ctx, &request);
    http_ctx->sysdep->service_sysdep_free(path);
    http_ctx->sysdep->service_sysdep_free(header);

    _service_tiot_http_exec_dec(http_ctx);

    return res;
}

int32_t tiot_http_recv(void *ctx)
{
    int32_t res = RET_SUCCESS;
    uint64_t timenow_ms = 0;
    service_http_response_t response;
    service_http_ctx_t *http_ctx = (service_http_ctx_t *)ctx;

    if (http_ctx == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }
    if (http_ctx->network_ctx == NULL) {
        return RET_SYS_DEPEND_NWK_CLOSED;
    }
    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    _service_tiot_http_exec_inc(http_ctx);

    memset(&response, 0, sizeof(service_http_response_t));
    if ((res = service_http_setopt(http_ctx, SERVICE_HTTPOPT_RECV_HANDLER, (void *)_service_http_recv_ctx)) < RET_SUCCESS ||
        (res = service_http_setopt(http_ctx, SERVICE_HTTPOPT_USERDATA, (void *)&response)) < RET_SUCCESS) {
        return res;
    }
    timenow_ms = http_ctx->sysdep->service_sysdep_time();
    while (1) {
        if (timenow_ms >= http_ctx->sysdep->service_sysdep_time()) {
            timenow_ms = http_ctx->sysdep->service_sysdep_time();
        }
        if (http_ctx->sysdep->service_sysdep_time() - timenow_ms >= http_ctx->recv_timeout_ms) {
            break;
        }

        res = service_http_recv(http_ctx);
        if (res < RET_SUCCESS) {
            break;
        }
    }

    if (res < RET_SUCCESS) {
        if (res != RET_HTTP_READ_BODY_FINISHED) {
            _service_tiot_http_exec_dec(http_ctx);
            if (response.content != NULL) {
                http_ctx->sysdep->service_sysdep_free(response.content);
            }
            return res;
        } else {
            res = RET_SUCCESS;
        }
    } else {
        return RET_HTTP_RECV_NOT_FINISHED;
    }

    _service_tiot_http_token_expired_event(http_ctx, &response);

    if (http_ctx->recv_ctx != NULL) {
        tiot_http_recv_t packet;

        packet.type = TIOT_HTTPRECV_BODY;
        packet.data.body.buffer = response.content;
        packet.data.body.len = response.content_len;

        http_ctx->recv_ctx(http_ctx, &packet, http_ctx->userdata);
    }
    if (response.content != NULL) {
        http_ctx->sysdep->service_sysdep_free(response.content);
    }

    _service_tiot_http_exec_dec(http_ctx);

    return res;
}

int32_t tiot_http_deinit(void **p_ctx)
{
    uint64_t deinit_timestart = 0;
    service_http_ctx_t *http_ctx = NULL;

    if (p_ctx == NULL || *p_ctx == NULL) {
        return RET_USER_INPUT_NULL_POINTER;
    }

    http_ctx = *(service_http_ctx_t **)p_ctx;

    if (http_ctx->exec_enabled == 0) {
        return RET_USER_INPUT_EXEC_DISABLED;
    }

    http_ctx->exec_enabled = 0;
    deinit_timestart = http_ctx->sysdep->service_sysdep_time();
    do {
        if (http_ctx->exec_count == 0) {
            break;
        }
        http_ctx->sysdep->service_sysdep_sleep(SERVICE_HTTP_DEINIT_INTERVAL_MS);
    } while ((http_ctx->sysdep->service_sysdep_time() - deinit_timestart) < http_ctx->deinit_timeout_ms);

    if (http_ctx->exec_count != 0) {
        return RET_HTTP_DEINIT_TIMEOUT;
    }

    if (http_ctx->product_key != NULL) {
        http_ctx->sysdep->service_sysdep_free(http_ctx->product_key);
    }
    if (http_ctx->device_name != NULL) {
        http_ctx->sysdep->service_sysdep_free(http_ctx->device_name);
    }
    if (http_ctx->device_secret != NULL) {
        http_ctx->sysdep->service_sysdep_free(http_ctx->device_secret);
    }
    if (http_ctx->extend_devinfo != NULL) {
        http_ctx->sysdep->service_sysdep_free(http_ctx->extend_devinfo);
    }
    if (http_ctx->token != NULL) {
        http_ctx->sysdep->service_sysdep_free(http_ctx->token);
    }

    service_http_deinit(p_ctx);

    return RET_SUCCESS;
}

int32_t tlink_sdk_http_init(tiot_http_config_t *http_config, void *http_recv_ctx, void *http_event_ctx)
{
    if(http_config == NULL|| http_recv_ctx == NULL || http_event_ctx == NULL){
        return RET_HTTP_INIT_ERROR;
    }
    
    service_http_ctx_t *http_ctx =NULL;
    int32_t res;
    http_config->port = 18000;
    http_config->http_host = "116.63.137.223";
    http_ctx = tiot_http_init();
    tiot_http_set_host(http_ctx, (void *)http_config->http_host);
    tiot_http_set_port(http_ctx, (void *)&(http_config->port));
    tiot_http_set_product_key(http_ctx, http_config->product_key);
    tiot_http_set_device_name(http_ctx, http_config->device_name);
    tiot_http_set_device_secret(http_ctx, http_config->device_secret);
    tiot_http_recv_ctx(http_ctx, http_recv_ctx);
    tiot_http_event_ctx(http_ctx, http_event_ctx);

    res = tiot_http_auth(http_ctx);

    tiot_http_deinit((void **)&http_ctx);
    return res;
}
